package org.jeecg.common.util;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;

import static java.util.stream.Collectors.groupingBy;

public class GroupUtil {


    public static <T, K> Map<String, ?> groupData(List<T> data, List<Function<T, K>> groupingFunctions) {
        if (groupingFunctions.isEmpty()) {
            return Collections.emptyMap();
        }

        // Create a copy of the groupingFunctions list to make it modifiable
        List<Function<T, K>> copyOfGroupingFunctions = new ArrayList<>(groupingFunctions);

        // Get the first grouping function and remove it from the copy
        Function<T, K> firstGroupingFunction = copyOfGroupingFunctions.remove(0);

        // Group the data using the first grouping function
        Map<K, List<T>> groupedData = data.stream()
                .collect(Collectors.groupingBy(firstGroupingFunction));

        // Recursively call the method for the remaining grouping functions
        Map<String, ?> result = groupedData.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> entry.getKey().toString(),
                        entry -> copyOfGroupingFunctions.isEmpty()
                                ? entry.getValue()
                                : groupData(entry.getValue(), copyOfGroupingFunctions)
                ));

        return result;
    }



    public static <T, R, U> List<JSONObject> groupAndCount(List<T> list, Function<T, R>... getFun) {
        Pair<Function<T, R>, Function<R, U>>[] pairs = Arrays.stream(getFun).map(e -> {
            return Pair.of(e, Function.identity());
        }).toArray(Pair[]::new);

        return groupAndCount(list, pairs);
    }

    public static <T, R, U> List<JSONObject> groupAndCount(List<T> list, Pair<Function<T, R>, Function<R, U>>... paris) {
        return Stream.iterate(0, (i) -> ++i).limit(paris.length).map(i -> {
            int label = i + 1;
            Pair<Function<T, R>, Function<R, U>> paris1 = paris[i];
            Function<T, R> getFun = paris1.getLeft();
            Function<R, U> reMappingFun = paris1.getRight();
            List<JSONObject> jsonObjects = groupAndCount(list, getFun, reMappingFun);
            return new JSONObject(new LinkedHashMap<>()) {{
                put("label", label);
                put("value", jsonObjects);
            }};
        }).collect(Collectors.toList());
    }

    public static <T, R, U> List<JSONObject> groupAndCount(List<T> list, Function<T, R> getFun, Function<R, U> reMappingFun) {
        Map<R, Long> collect = list.stream().filter(e -> ObjectUtil.isNotEmpty(getFun.apply(e))).collect(Collectors.groupingBy(
            getFun
            , Collectors.counting()));
        return collect.entrySet().stream().map(e -> {
            R x = e.getKey();
            U apply = reMappingFun.apply(x);
            return new JSONObject(new LinkedHashMap<>()) {{
                put("x", apply);
                put("y", e.getValue());
            }};

        }).collect(Collectors.toList());
    }
}
